package cn.gotom.svg;

import java.util.Iterator;

import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Matrix;
import android.graphics.Paint;
import android.graphics.RectF;
import cn.gotom.svg.xml.StyleAttribute;
import cn.gotom.svg.xml.XMLParseUtil;

/**
 * @author 裴绍国
 * @author <a href="mailto:peixere@gmail.com">裴绍国</a>
 */
abstract public class RenderableElement extends TransformableElement
{

	/**
	 * 
	 */
	private static final long serialVersionUID = 1L;
	public static final int VECTOR_EFFECT_NONE = 0;
	public static final int VECTOR_EFFECT_NON_SCALING_STROKE = 1;

	int vectorEffect;
	protected Matrix cachedMatrix = null;
	protected final Paint paint = new Paint();

	public RenderableElement()
	{
	}

	public RenderableElement(String id, SVGElement parent)
	{
		super(id, parent);
	}

	protected void build() throws SVGException
	{
		super.build();

		StyleAttribute sty = getAttribute("vector-effect");
		if (sty != null)
		{
			if ("non-scaling-stroke".equals(sty.getStringValue()))
			{
				vectorEffect = VECTOR_EFFECT_NON_SCALING_STROKE;
			}
			else
			{
				vectorEffect = VECTOR_EFFECT_NONE;
			}
		}
		else
		{
			vectorEffect = VECTOR_EFFECT_NONE;
		}
	}

	protected void beginLayer(Canvas canvas) throws SVGException
	{
		if (xform != null)
		{
			canvas.save();
			canvas.concat(xform);
			// cachedMatrix = canvas.getMatrix();
			// canvas.setMatrix(xform);
		}
	}

	protected void finishLayer(Canvas canvas)
	{
		if (xform != null)
		{
			canvas.restore();
		}
		// if (cachedMatrix != null)
		// {
		// canvas.setMatrix(cachedMatrix);
		// }
	}

	protected float getStrokeWidth()
	{
		return 1F;
	}

	public void render(Canvas canvas) throws SVGException
	{
		if (log.isDebugEnabled())
		{
			log.debug(this);
		}
		if (!visible())
		{
			return;
		}
		beginLayer(canvas);
		Iterator<SVGElement> it = children.iterator();
		while (it.hasNext())
		{
			SVGElement ele = (SVGElement) it.next();
			if (ele instanceof RenderableElement)
			{
				RenderableElement rendEle = (RenderableElement) ele;
				rendEle.render(canvas);
			}
		}
		finishLayer(canvas);
	}

	protected boolean visible()
	{
		StyleAttribute styleAttrib = new StyleAttribute();
		if (getAttribute(styleAttrib.setName(StyleAttribute.style)))
		{
			String style = styleAttrib.getStringValue();
			if (style != null)
			{
				XMLParseUtil.parseStyle(style, attributes);
			}
		}

		if (getAttribute(styleAttrib.setName("visibility")))
		{
			if (styleAttrib.getStringValue().equals("hidden"))
				return false;
		}
		if (getAttribute(styleAttrib.setName("display")))
		{
			if (styleAttrib.getStringValue().equals("none"))
				return false;
		}
		return true;
	}

	public boolean contains(float x, float y)
	{
		if (getBoundingBox() != null)
			return getBoundingBox().contains(x, y);
		return false;
	}

	public abstract RectF getBoundingBox();

	protected RectF includeStrokeInBounds(RectF rect)
	{
		StyleAttribute styleAttrib = new StyleAttribute();
		if (!getAttribute(styleAttrib.setName("stroke")))
			return rect;

		float strokeWidth = 1;
		if (getAttribute(styleAttrib.setName("stroke-width")))
			strokeWidth = styleAttrib.getFloatValue();
		RectF bounds = new RectF();
		bounds.set(rect.left - strokeWidth / 2, rect.top - strokeWidth / 2, rect.right + strokeWidth, rect.bottom + strokeWidth);
		return bounds;
	}

	protected void doOpacity(boolean fillMode)
	{
		StyleAttribute attrib = new StyleAttribute();
		Float opacity = 1F;
		if (getAttribute(attrib.setName(StyleAttribute.opacity)))
		{
			opacity = attrib.getFloatValue();
		}
		else if (fillMode ? getAttribute(attrib.setName(StyleAttribute.fillOpacity)) : getAttribute(attrib.setName(StyleAttribute.strokeOpacity)))
		{
			opacity = attrib.getFloatValue();
		}
		paint.setAlpha((int) (255 * opacity));
	}

	protected void doFillPaint()
	{
		paint.reset();
		paint.setShader(null);
		paint.setAntiAlias(true);
		paint.setColor(Color.BLACK);
		paint.setStrokeWidth(getStrokeWidth());
		StyleAttribute attrib = new StyleAttribute();
		paint.setStyle(Paint.Style.FILL);
		if (getAttribute(attrib.setName(StyleAttribute.fill)))
		{
			String fillString = attrib.getStringValue();
			if (fillString != null && fillString.startsWith("url(#"))
			{
				// String id = fillString.substring("url(#".length(), fillString.length() - 1);
				// Shader shader = gradients.get(id);
				// if (shader != null)
				// {
				// paint.setShader(shader);
				// }
			}
			else
			{
				Integer color = attrib.getColorValue();
				if (color != null)
					paint.setColor(color);
			}
		}
		doOpacity(true);
	}

	protected boolean doStrokePaint()
	{
		paint.reset();
		paint.setShader(null);
		paint.setAntiAlias(true);
		paint.setColor(Color.BLACK);
		paint.setStrokeWidth(getStrokeWidth());
		StyleAttribute attrib = new StyleAttribute();
		paint.setStyle(Paint.Style.STROKE);
		if (getAttribute(attrib.setName(StyleAttribute.stroke)))
		{
			Integer color = attrib.getColorValue();
			if (color != null)
			{
				paint.setColor(color);
			}
			else
			{
				return false;
			}
		}
		else
		{
			return false;
		}
		if (getAttribute(attrib.setName(StyleAttribute.strokeWidth)))
		{
			paint.setStrokeWidth(attrib.getFloatValue());
		}
		if (paint.getStrokeWidth() == 0f)
			return false;
		paint.setStrokeCap(Paint.Cap.BUTT);
		if (getAttribute(attrib.setName("stroke-linecap")))
		{
			String linecap = attrib.getStringValue();
			if ("round".equals(linecap))
			{
				paint.setStrokeCap(Paint.Cap.ROUND);
			}
			else if ("square".equals(linecap))
			{
				paint.setStrokeCap(Paint.Cap.SQUARE);
			}
			else if ("butt".equals(linecap))
			{
				paint.setStrokeCap(Paint.Cap.BUTT);
			}
		}
		paint.setStrokeJoin(Paint.Join.MITER);
		if (getAttribute(attrib.setName("stroke-linejoin")))
		{
			String linejoin = attrib.getStringValue();
			if ("miter".equals(linejoin))
			{
				paint.setStrokeJoin(Paint.Join.MITER);
			}
			else if ("round".equals(linejoin))
			{
				paint.setStrokeJoin(Paint.Join.ROUND);
			}
			else if ("bevel".equals(linejoin))
			{
				paint.setStrokeJoin(Paint.Join.BEVEL);
			}
		}
		doOpacity(false);
		return true;
	}
}
